package com.neuronbit.businessflow.runtime;

import com.neuronbit.businessflow.runtime.lifecycle.FlowLifecycleHook;
import com.neuronbit.businessflow.runtime.lifecycle.StepLifecycleHook;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;

/**
 * The type Step.
 *
 * @param <T> the type parameter
 */
@Getter
public abstract class Step<T extends FlowContext> {
    /**
     * The Log.
     */
    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    private final String id;
    private final String name;
    private final String description;
    private final StepType type;

    /**
     * Instantiates a new Step.
     *
     * @param id          the id
     * @param name        the name
     * @param description the description
     */
    public Step(String id, String name, String description, StepType type) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.type = type;
    }

    /**
     * Call.
     *
     * @param context the context
     * @param flow    the flow
     * @throws Exception the exception
     */
    public abstract void call(T context, Flow flow) throws Exception;

    /**
     * Start.
     *
     * @param context the context
     * @param flow    the flow
     * @throws Exception the exception
     */
    public void start(T context, Flow flow) throws Exception {
        Collection<StepLifecycleHook> stepHooks = flow.getStepBeanProvider().getStepHooks();

        for (StepLifecycleHook stepLifecycleHook : stepHooks) {
            if (stepLifecycleHook.support(context, flow, this)) {
                stepLifecycleHook.onStart(context, flow, this);
            }
        }
    }

    /**
     * Error.
     *
     * @param context the context
     * @param flow    the flow
     * @param e       the e
     * @throws Exception the exception
     */
    public void error(T context, Flow flow, Exception e) throws Exception {
        Collection<StepLifecycleHook> stepHooks = flow.getStepBeanProvider().getStepHooks();
        Collection<FlowLifecycleHook> flowHooks = flow.getStepBeanProvider().getFlowHooks();

        for (StepLifecycleHook stepLifecycleHook : stepHooks) {
            if (stepLifecycleHook.support(context, flow, this)) {
                stepLifecycleHook.onError(context, flow, this, e);
            }
        }
        //call step before flow
        for (FlowLifecycleHook flowLifecycleHook : flowHooks) {
            if (flowLifecycleHook.support(context, flow)) {
                flowLifecycleHook.onError(context, flow, e);
            }
        }
    }

    /**
     * Error.
     *
     * @param context the context
     * @param flow    the flow
     * @throws Exception the exception
     */
    public void pause(T context, Flow flow) throws Exception {
        Collection<StepLifecycleHook> stepHooks = flow.getStepBeanProvider().getStepHooks();
        Collection<FlowLifecycleHook> flowHooks = flow.getStepBeanProvider().getFlowHooks();

        for (StepLifecycleHook stepLifecycleHook : stepHooks) {
            if (stepLifecycleHook.support(context, flow, this)) {
                stepLifecycleHook.onPause(context, flow, this);
            }
        }
        //call step before flow
        for (FlowLifecycleHook flowLifecycleHook : flowHooks) {
            if (flowLifecycleHook.support(context, flow)) {
                flowLifecycleHook.onPause(context, flow);
            }
        }
    }

    /**
     * End.
     *
     * @param context the context
     * @param flow    the flow
     * @throws Exception the exception
     */
    public void end(T context, Flow flow) throws Exception {
        Collection<StepLifecycleHook> stepHooks = flow.getStepBeanProvider().getStepHooks();

        for (StepLifecycleHook stepLifecycleHook : stepHooks) {
            if (stepLifecycleHook.support(context, flow, this)) {
                stepLifecycleHook.onEnd(context, flow, this);
            }
        }
    }

    /**
     * skipped.
     *
     * @param context the context
     * @param flow    the flow
     * @throws Exception the exception
     */
    public void skipped(T context, Flow flow) throws Exception {
        Collection<StepLifecycleHook> stepHooks = flow.getStepBeanProvider().getStepHooks();

        for (StepLifecycleHook stepLifecycleHook : stepHooks) {
            if (stepLifecycleHook.support(context, flow, this)) {
                stepLifecycleHook.onSkipped(context, flow, this);
            }
        }
    }
}
